using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.Configuration;
using System.Configuration;
using System.Globalization;
using System.Text;
using System.Collections.Specialized;

/// <summary>
/// A page which guides the user through setup.
/// </summary>
public partial class Install : LocalPage
{
    /// <summary>
    /// Attempts to fill text box values from the current settings on first load.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
    protected void Page_Load(object sender, EventArgs e)
    {
        bool webPI = false;
        pnlError.Visible = false;
        if(WebConfigurationManager.AppSettings.AllKeys.Contains("salt") && WebConfigurationManager.AppSettings["salt"].Length > 0 && WebConfigurationManager.AppSettings["salt"] != "0") {
            // salt already set and not first time install
            Response.Redirect("~/Default.aspx");
        } else {
            if(!Page.IsPostBack) {
                Session["pw"] = string.Empty;
                litDashboardLink.Text = Utils.FormatLink(Resources.UIStrings.InstallDashboardLink, ResolveUrl("~/Default.aspx"));
                litWikiLink.Text = Utils.FormatLink(Resources.UIStrings.InstallWikiLink, "http://wiki.lime49.com/index.php?title=Quickstart_(ResourceBlender.NET)");
                if(bool.TryParse(WebConfigurationManager.AppSettings["webpi"], out webPI) && webPI) {
                    InstallFromWebPI();
                    Master.Page.Form.DefaultButton = wzdInstall.FindControl("StepNavigationTemplateContainerID$StepNextButton").UniqueID;
                } else {
                    wzdInstall.ActiveStepIndex = 0;
                    Master.Page.Form.DefaultButton = wzdInstall.FindControl("StartNavigationTemplateContainerID$StartNextButton").UniqueID;
                }
            } else {
                switch(wzdInstall.ActiveStepIndex) { // set the default button to next
                    case 0:
                        Master.Page.Form.DefaultButton = wzdInstall.FindControl("StepNavigationTemplateContainerID$StepNextButton").UniqueID;
                        break;
                    case 1:
                        Master.Page.Form.DefaultButton = wzdInstall.FindControl("FinishNavigationTemplateContainerID$FinishButton").UniqueID;
                        break;
                    default:
                        Master.Page.Form.DefaultButton = wzdInstall.FindControl("StepNavigationTemplateContainerID$StepNextButton").UniqueID;
                        break;
                }
            }
        }
    }

    /// <summary>
    /// Finalizes an installation from the Web PI.
    /// </summary>
    private void InstallFromWebPI() {
        System.Configuration.Configuration config;
        KeyValueConfigurationCollection appSettings;
        Database db = null;
        string newSalt = string.Empty;
        try {
            db = new Database();
            db.OpenConnection();
            config = WebConfigurationManager.OpenWebConfiguration("~");
            appSettings = config.AppSettings.Settings;
            string oldSalt = appSettings["salt"].Value;
            newSalt = Utils.GenID(true);
            // values such as the port can be changed later if necessary, they are only saved as settings to prevent parsing of the connectionstring
            if(appSettings.AllKeys.Contains("salt")) {
                appSettings.Remove("salt");
            }
            appSettings.Add("salt", newSalt); // save the salt to prevent access to the installer
            appSettings["webpi"].Value = "false";
            using(Database.Command cmd = db.CreateCommand()) {
                cmd.CommandText = string.Format("SELECT password FROM {0}users WHERE permissions = -1", db.TablePrefix);
                object res = cmd.ExecuteScalar();
                if(res != null) { // password already salted and permissions set
                    string plainTextPassword = res.ToString();
                    cmd.CommandText = string.Format("UPDATE {0}users SET password = @pass, permissions = @permissions WHERE permissions = -1", db.TablePrefix);
                    cmd.AddParameterWithValue("@permissions", ((int)Constants.Permissions.ChangeSettings).ToString());
                    try {
                        config.Save(); // if this succeeds we have write access to web.config
                        cmd.AddParameterWithValue("@pass", Utils.SaltPassword(plainTextPassword, newSalt));
                        cmd.ExecuteNonQuery();
                    } catch { // can't modify web.config, salt the password with the existing value
                        cmd.AddParameterWithValue("@pass", Utils.SaltPassword(plainTextPassword, oldSalt));
                        cmd.ExecuteNonQuery();
                    }
                    wzdInstall.ActiveStepIndex = 2;
                }
            }
        } catch(System.Configuration.ConfigurationErrorsException cEx) {
            ConnectionStringSettings connStrings = WebConfigurationManager.ConnectionStrings["Default"];
            config = WebConfigurationManager.OpenWebConfiguration("~");
            appSettings = config.AppSettings.Settings;
            Dictionary<string, string> appSettingsKeys = new Dictionary<string, string>() {
                { "dbtype", appSettings["dbtype"].Value },
                { "dbhost", appSettings["db"].Value },
                { "dbport", appSettings["dbport"].Value },
                { "dbuser", appSettings["dbuser"].Value },
                { "dbpass", appSettings["dbpass"].Value },
                { "dbdatabase", appSettings["dbdatabase"].Value },
                { "tableprefix", appSettings["tableprefix"].Value },
                { "salt", newSalt},
                { "webpi", "true" }
            };
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "401a", Resources.Errors.ErrorneousConfiguration + " - " + cEx.Message) + "<br /><br />" + Utils.GetWebConfig(appSettingsKeys, connStrings.ConnectionString, connStrings.ProviderName);
            pnlError.Visible = true;
        } catch(System.UnauthorizedAccessException uEx) {
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "402a", Resources.Errors.ASPNETAccountNoAccess+ " - " + uEx.Message);
            pnlError.Visible = true;
        } catch(InvalidOperationException iEx) {
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "403a", iEx.Message);
            pnlError.Visible = true;
        } catch(MySql.Data.MySqlClient.MySqlException mEx) {
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "404a", Resources.Errors.CouldNotConnectMySQL+ " - " + mEx.Message);
            pnlError.Visible = true;
        } catch(ResourceBlenderException rEx) {
            if(rEx.ErrorCode == 0) {
                lblError.Text = Resources.Errors.Error + ": " + rEx.Message;
            } else {
                lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, rEx.ErrorCode, rEx.Message);
            }
            pnlError.Visible = true;
        } catch(Exception ex) {
            lblError.Text = Resources.Errors.Error + ": " + Resources.Errors.CouldNotConnectDatabase + " - " + ex.Message;
            pnlError.Visible = true;
        } finally {
            if(db != null && db.Connected) {
                try {
                    db.CloseConnection();
                } catch { }
            }
        }
    }

    /// <summary>
    /// Saves the settings entered at each stage.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="System.Web.UI.WebControls.WizardNavigationEventArgs"/> instance containing the event data.</param>
    protected void wzdInstall_NextButtonClick(object sender, WizardNavigationEventArgs e) {
        string salt = string.Empty;
        try {
            Database db;
            Database.DatabaseType dbType = dbSelector.Mode;
            string connectionString = dbSelector.GetConnectionString(),
                   tablePrefix = dbSelector.TablePrefix;
            System.Configuration.ConnectionStringSettings conn = null;
            System.Configuration.Configuration config;
            KeyValueConfigurationCollection appSettings;
            switch(e.CurrentStepIndex) {
                case 0:
                    if(dbSelector.ValidateInput()) {
                        db = new Database(dbType, connectionString, tablePrefix);
                        Session["pw"] = dbSelector.Password;
                        try {
                            db.CreateTables(true, true, true, true, true, true);
                        } catch(Exception tEx) {
                            lblError.Text = string.Format(Resources.Errors.ErrorCreatingTables, tEx.Message);
                            pnlError.Visible = true;
                        }
                    } else {
                        e.Cancel = true;
                    }
                    break;
                case 1:
                    config = WebConfigurationManager.OpenWebConfiguration("~");
                    appSettings = config.AppSettings.Settings;
                    dbSelector.Password = Session["pw"].ToString();
                    connectionString = dbSelector.GetConnectionString();
                    db = new Database(dbType, connectionString, tablePrefix);
                    switch(dbSelector.Mode) {
                        case Database.DatabaseType.MySQL:
                            conn = new ConnectionStringSettings("Default", connectionString, "MySql.Data.MySqlClient");
                            break;
                        case Database.DatabaseType.SQLServer:
                            conn = new ConnectionStringSettings("Default", connectionString, "System.Data.SqlClient");
                            break;
                    }
                    config.ConnectionStrings.ConnectionStrings.Clear();
                    if(config.ConnectionStrings.ConnectionStrings.IndexOf(conn) >= 0) {
                        config.ConnectionStrings.ConnectionStrings.Remove("Default");
                    }
                    config.ConnectionStrings.ConnectionStrings.Add(conn);
                    appSettings["dbtype"].Value = ((int)dbSelector.Mode).ToString();
                    appSettings["dbhost"].Value = dbSelector.Host;
                    appSettings["dbport"].Value = dbSelector.Port.ToString();
                    appSettings["dbuser"].Value = dbSelector.User;
                    appSettings["dbpass"].Value = dbSelector.Password;
                    appSettings["dbdatabase"].Value = dbSelector.DatabaseName;
                    appSettings["tableprefix"].Value = dbSelector.TablePrefix;
                    using(Database.Command cmd = db.CreateCommand()) {
                        salt = Utils.GenID(true);
                        db.OpenConnection();
                        cmd.CommandText = "DELETE FROM " + tablePrefix + "users WHERE username = @user";
                        cmd.AddParameterWithValue("@user", txtAdminUsername.Text);
                        cmd.ExecuteNonQuery();
                        cmd.CommandText = "INSERT INTO " + tablePrefix + "users (username, password, permissions) VALUES (@user, @password, @permissions)";
                        cmd.AddParameterWithValue("@password", Utils.SaltPassword(txtAdminPassword.Text, salt));
                        cmd.AddParameterWithValue("@permissions", ((int)Constants.Permissions.ChangeSettings).ToString());
                        cmd.ExecuteNonQuery();
                        if(appSettings.AllKeys.Contains("salt")) {
                            appSettings.Remove("salt");
                        }
                        appSettings.Add("salt", salt); // now save the salt to prevent access to the installer
                        db.AddDummyData();
                        config.Save();

                        config = WebConfigurationManager.OpenWebConfiguration("~");
                        Session.Remove("connectionstring");
                    }
                    break;
            }
        } catch(System.Configuration.ConfigurationErrorsException cEx) {
            Database.DatabaseType dbType = dbSelector.Mode;
            string providerName = dbType == Database.DatabaseType.MySQL ? "MySql.Data.MySqlClient" : "System.Data.SqlClient";
            string connectionString = Database.GetConnectionString(dbType, dbSelector.Host, dbSelector.Port, dbSelector.User, Session["pw"].ToString(), dbSelector.DatabaseName);
            Dictionary<string, string> appSettings = new Dictionary<string, string>() {
                { "dbtype", ((int)dbType).ToString() },
                { "dbhost", dbSelector.Host },
                { "dbport", dbSelector.Port.ToString() },
                { "dbuser", dbSelector.User },
                { "dbpass", dbSelector.Password },
                { "dbdatabase", dbSelector.DatabaseName },
                { "tableprefix", dbSelector.TablePrefix },
                { "salt", salt },
                { "webpi", "false" }
            };
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "401a", Resources.Errors.ErrorneousConfiguration + " - " + cEx.Message) + "<br /><br />" + Utils.GetWebConfig(appSettings, connectionString, providerName);
            pnlError.Visible = true;
        } catch(System.UnauthorizedAccessException uEx) {
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "402", Resources.Errors.ASPNETAccountNoAccess + " - " + uEx.Message);
            pnlError.Visible = true;
            e.Cancel = true;
        } catch(InvalidOperationException iEx) {
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "403", iEx.Message);
            pnlError.Visible = true;
            e.Cancel = true;
        } catch(MySql.Data.MySqlClient.MySqlException mEx) {
            lblError.Text = string.Format(Resources.Errors.ErrorNumDescription, "404", Resources.Errors.CouldNotConnectMySQL + " - " + mEx.Message);
            pnlError.Visible = true;
            e.Cancel = true;
        } catch(Exception ex) {
            lblError.Text = Resources.Errors.Error + ": " + Resources.Errors.CouldNotConnectDatabase + " - " + ex.Message;
            pnlError.Visible = true;
            e.Cancel = true;
        }
    }
}
